<form class="media-focal-point-container {className}"
      aria-label="{intl.enterFocalPoint}"
      on:resize="measure()"
>
  <div class="media-focal-point-image-container" ref:container>
    <img
      width={intrinsicWidth}
      height={intrinsicHeight}
      class="media-focal-point-image"
      src={previewSrc}
      alt={shortName}
      on:load="onImageLoad()"
    />
    <div class="media-focal-point-backdrop"></div>
    <div class="media-draggable-area"
         style={draggableAreaStyle}
    >
      <!-- 52px == 32px icon width + 10px padding -->
      <Draggable
        draggableClass="media-draggable-area-inner"
        indicatorClass="media-focal-point-indicator {imageLoaded ? '': 'hidden'} {dragging ? 'dragging' : ''}"
        indicatorWidth={52}
        indicatorHeight={52}
        x={indicatorX}
        y={indicatorY}
        on:dragStart="onDragStart()"
        on:dragEnd="onDragEnd()"
        on:change="onDraggableChange(event)"
      >
        <SvgIcon
          className="media-focal-point-indicator-svg"
          href="#fa-crosshairs"
        />
      </Draggable>
    </div>
  </div>
  <div class="media-focal-point-inputs">
    <div class="media-focal-point-input-pair">
      <label for="media-focal-point-x-input-{realm}">
        X
      </label>
      <input type="number"
             step="0.01"
             min="-1"
             max="1"
             inputmode="decimal"
             placeholder="0"
             id="media-focal-point-x-input-{realm}"
             bind:value="rawFocusX"
      />
    </div>
    <div class="media-focal-point-input-pair">
      <label for="media-focal-point-y-input-{realm}">
        Y
      </label>
      <input type="number"
             step="0.01"
             min="-1"
             max="1"
             inputmode="decimal"
             placeholder="0"
             id="media-focal-point-y-input-{realm}"
             bind:value="rawFocusY"
      />
    </div>
  </div>
</form>
<style>
  .media-focal-point-container {
    display: flex;
    flex-direction: column;
  }
  .media-focal-point-image-container {
    flex: 1;
    width: 100%;
    position: relative;
    min-height: 0;
  }
  .media-focal-point-image {
    object-fit: contain;
    width: 100%;
    height: 100%;
  }

  .media-focal-point-backdrop {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    top: 0;
  }

  @supports (-webkit-backdrop-filter: blur(1px) saturate(1%)) or (backdrop-filter: blur(1px) saturate(1%)) {
    .media-focal-point-backdrop {
      -webkit-backdrop-filter: blur(1px) saturate(105%);
      backdrop-filter: blur(1px) saturate(105%);
      background-color: var(--focal-img-backdrop-filter);
    }
  }

  @supports not ((-webkit-backdrop-filter: blur(1px) saturate(1%)) or (backdrop-filter: blur(1px) saturate(1%))) {
    .media-focal-point-backdrop {
      background-color: var(--focal-img-bg);
    }
  }

  .media-focal-point-inputs {
    display: flex;
    padding: 10px;
    justify-content: space-around;
    width: auto;
  }

  .media-focal-point-input-pair {
    display: flex;
    align-items: center;
  }

  .media-focal-point-input-pair:first-child {
    margin-right: 10px;
  }

  .media-focal-point-input-pair input {
    margin-left: 10px;
  }

  .media-draggable-area {
    position: absolute;
  }

  :global(.media-focal-point-indicator) {
    background: var(--focal-bg);
    border-radius: 100%;
    display: flex;
  }

  :global(.media-focal-point-indicator:hover) {
    background: var(--focal-bg-hover);
  }

  :global(.media-focal-point-indicator.dragging) {
    background: var(--focal-bg-drag);
  }

  :global(.media-draggable-area-inner) {
    width: 100%;
    height: 100%;
  }

  :global(.media-focal-point-indicator-svg) {
    width: 32px;
    height: 32px;
    padding: 15px;
    fill: var(--focal-color);
  }

  @media (max-width: 767px) {
    .media-focal-point-inputs {
      padding: 5px 20px;
      justify-content: space-around;
    }
    :global(.media-focal-point-indicator-svg) {
      width: 32px;
      height: 32px;
      padding: 12px;
      fill: var(--focal-color);
    }
    .media-focal-point-input-pair label {
      font-size: 1.1em;
    }
    .media-focal-point-input-pair input {
      font-size: 1.1em;
    }
  }
</style>
<script>
  import { store } from '../../../_store/store.js'
  import { get } from '../../../_utils/lodash-lite.js'
  import { observe } from 'svelte-extras'
  import { scheduleIdleTask } from '../../../_utils/scheduleIdleTask.js'
  import { coordsToPercent, percentToCoords } from '../../../_utils/coordsToPercent.js'
  import SvgIcon from '../../SvgIcon.html'
  import { intrinsicScale } from '../../../_thirdparty/intrinsic-scale/intrinsicScale.js'
  import { resize } from '../../../_utils/events.js'
  import Draggable from '../../Draggable.html'

  const parseAndValidateFloat = rawText => {
    let float = parseFloat(rawText)
    if (Number.isNaN(float)) {
      float = 0
    }
    float = Math.min(1, float)
    float = Math.max(-1, float)
    float = Math.round(float * 100) / 100
    return float
  }

  export default {
    oncreate () {
      this.setupSyncFromStore()
      this.setupSyncToStore()
    },
    components: {
      SvgIcon,
      Draggable
    },
    data: () => ({
      dragging: false,
      rawFocusX: '0',
      rawFocusY: '0',
      containerWidth: 0,
      containerHeight: 0,
      imageLoaded: false,
      className: ''
    }),
    store: () => store,
    computed: {
      mediaItem: ({ media, index }) => get(media, [index]),
      focusX: ({ mediaItem }) => get(mediaItem, ['focusX'], 0),
      focusY: ({ mediaItem }) => get(mediaItem, ['focusY'], 0),
      previewSrc: ({ mediaItem }) => mediaItem.data.preview_url,
      nativeWidth: ({ mediaItem }) => (
        get(mediaItem, ['data', 'meta', 'original', 'width'], 300) // TODO: Pleroma placeholder
      ),
      nativeHeight: ({ mediaItem }) => (
        get(mediaItem, ['data', 'meta', 'original', 'height'], 200) // TODO: Pleroma placeholder
      ),
      shortName: ({ mediaItem }) => (
        // sometimes we no longer have the file, e.g. in a delete and redraft situation,
        // so fall back to the description if it was provided
        get(mediaItem, ['file', 'name']) || get(mediaItem, ['description']) || 'media'
      ),
      // intrinsic width/height to avoid layout shifting https://chromestatus.com/feature/5695266130755584
      // note pleroma does not give us intrinsic width/height
      intrinsicWidth: ({ mediaItem }) => get(mediaItem, ['data', 'meta', 'original', 'width']),
      intrinsicHeight: ({ mediaItem }) => get(mediaItem, ['data', 'meta', 'original', 'height']),
      scale: ({ nativeWidth, nativeHeight, containerWidth, containerHeight }) => (
        intrinsicScale(containerWidth, containerHeight, nativeWidth, nativeHeight)
      ),
      scaleWidth: ({ scale }) => scale.width,
      scaleHeight: ({ scale }) => scale.height,
      scaleX: ({ scale }) => scale.x,
      scaleY: ({ scale }) => scale.y,
      indicatorX: ({ focusX }) => (coordsToPercent(focusX) / 100),
      indicatorY: ({ focusY }) => ((100 - coordsToPercent(focusY)) / 100),
      draggableAreaStyle: ({ scaleWidth, scaleHeight, scaleX, scaleY }) => (
        `top: ${scaleY}px; left: ${scaleX}px; width: ${scaleWidth}px; height: ${scaleHeight}px;`
      )
    },
    methods: {
      observe,
      setupSyncFromStore () {
        this.observe('mediaItem', mediaItem => {
          const { rawFocusX, rawFocusY } = this.get()

          const syncFromStore = (rawKey, rawFocus, key) => {
            const focus = get(mediaItem, [key], 0) || 0
            const focusAsString = focus.toString()
            if (focusAsString !== rawFocus) {
              this.set({ [rawKey]: focusAsString })
            }
          }

          syncFromStore('rawFocusX', rawFocusX, 'focusX')
          syncFromStore('rawFocusY', rawFocusY, 'focusY')
        })
      },
      setupSyncToStore () {
        const observeAndSync = (rawKey, key) => {
          this.observe(rawKey, rawFocus => {
            const { realm, index, media } = this.get()
            const rawFocusDecimal = parseAndValidateFloat(rawFocus)
            if (media[index][key] !== rawFocusDecimal) {
              media[index][key] = rawFocusDecimal
              this.store.setComposeData(realm, { media })
              scheduleIdleTask(() => this.store.save())
            }
          }, { init: false })
        }

        observeAndSync('rawFocusX', 'focusX')
        observeAndSync('rawFocusY', 'focusY')
      },
      onDraggableChange ({ x, y }) {
        scheduleIdleTask(() => {
          const focusX = parseAndValidateFloat(percentToCoords(x * 100))
          const focusY = parseAndValidateFloat(percentToCoords(100 - (y * 100)))
          const { realm, index, media } = this.get()
          if (media[index].focusX !== focusX || media[index].focusY !== focusY) {
            media[index].focusX = focusX
            media[index].focusY = focusY
            this.store.setComposeData(realm, { media })
            scheduleIdleTask(() => this.store.save())
          }
        })
      },
      onDragStart () {
        this.set({ dragging: true })
      },
      onDragEnd () {
        this.set({ dragging: false })
      },
      measure () {
        requestAnimationFrame(() => {
          if (!this.refs.container) {
            return
          }
          const rect = this.refs.container.getBoundingClientRect()
          this.set({
            containerWidth: rect.width,
            containerHeight: rect.height
          })
        })
      },
      onImageLoad () {
        this.measure()
        this.set({ imageLoaded: true })
      }
    },
    events: {
      resize
    }
  }
</script>
